1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.solr.schema;
19
20 import javax.xml.xpath.XPath;
21 import javax.xml.xpath.XPathConstants;
22 import javax.xml.xpath.XPathExpressionException;
23 import java.io.IOException;
24 import java.io.Writer;
25 import java.lang.invoke.MethodHandles;
26 import java.util.*;
27 import java.util.regex.Pattern;
28
29 import org.apache.lucene.analysis.Analyzer;
30 import org.apache.lucene.analysis.DelegatingAnalyzerWrapper;
31 import org.apache.lucene.index.DocValuesType;
32 import org.apache.lucene.index.FieldInfo;
33 import org.apache.lucene.index.IndexOptions;
34 import org.apache.lucene.index.IndexReader;
35 import org.apache.lucene.index.IndexableField;
36 import org.apache.lucene.index.MultiFields;
37 import org.apache.lucene.search.similarities.Similarity;
38 import org.apache.lucene.uninverting.UninvertingReader;
39 import org.apache.lucene.util.Version;
40 import org.apache.solr.common.SolrException;
41 import org.apache.solr.common.SolrException.ErrorCode;
42 import org.apache.solr.common.params.SolrParams;
43 import org.apache.solr.common.util.NamedList;
44 import org.apache.solr.common.util.SimpleOrderedMap;
45 import org.apache.solr.core.Config;
46 import org.apache.solr.core.SolrConfig;
47 import org.apache.solr.core.SolrResourceLoader;
48 import org.apache.solr.request.LocalSolrQueryRequest;
49 import org.apache.solr.response.SchemaXmlWriter;
50 import org.apache.solr.response.SolrQueryResponse;
51 import org.apache.solr.search.similarities.ClassicSimilarityFactory;
52 import org.apache.solr.util.DOMUtil;
53 import org.apache.solr.util.plugin.SolrCoreAware;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.w3c.dom.Document;
57 import org.w3c.dom.Element;
58 import org.w3c.dom.NamedNodeMap;
59 import org.w3c.dom.Node;
60 import org.w3c.dom.NodeList;
61 import org.xml.sax.InputSource;
62
63 import static java.util.Collections.singletonList;
64 import static java.util.Collections.singletonMap;
65
66
67
68
69
70
71
72 public class IndexSchema {
73 public static final String COPY_FIELD = "copyField";
74 public static final String COPY_FIELDS = COPY_FIELD + "s";
75 public static final String DEFAULT_OPERATOR = "defaultOperator";
76 public static final String DEFAULT_SCHEMA_FILE = "schema.xml";
77 public static final String DEFAULT_SEARCH_FIELD = "defaultSearchField";
78 public static final String DESTINATION = "dest";
79 public static final String DYNAMIC_FIELD = "dynamicField";
80 public static final String DYNAMIC_FIELDS = DYNAMIC_FIELD + "s";
81 public static final String FIELD = "field";
82 public static final String FIELDS = FIELD + "s";
83 public static final String FIELD_TYPE = "fieldType";
84 public static final String FIELD_TYPES = FIELD_TYPE + "s";
85 public static final String INTERNAL_POLY_FIELD_PREFIX = "*" + FieldType.POLY_FIELD_SEPARATOR;
86 public static final String LUCENE_MATCH_VERSION_PARAM = "luceneMatchVersion";
87 public static final String MAX_CHARS = "maxChars";
88 public static final String NAME = "name";
89 public static final String REQUIRED = "required";
90 public static final String SCHEMA = "schema";
91 public static final String SIMILARITY = "similarity";
92 public static final String SLASH = "/";
93 public static final String SOLR_QUERY_PARSER = "solrQueryParser";
94 public static final String SOURCE = "source";
95 public static final String TYPE = "type";
96 public static final String TYPES = "types";
97 public static final String UNIQUE_KEY = "uniqueKey";
98 public static final String VERSION = "version";
99
100 private static final String AT = "@";
101 private static final String DESTINATION_DYNAMIC_BASE = "destDynamicBase";
102 private static final String SOLR_CORE_NAME = "solr.core.name";
103 private static final String SOURCE_DYNAMIC_BASE = "sourceDynamicBase";
104 private static final String SOURCE_EXPLICIT_FIELDS = "sourceExplicitFields";
105 private static final String TEXT_FUNCTION = "text()";
106 private static final String XPATH_OR = " | ";
107
108 private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
109 protected final SolrConfig solrConfig;
110 protected String resourceName;
111 protected String name;
112 protected float version;
113 protected final SolrResourceLoader loader;
114
115 protected Map<String,SchemaField> fields = new HashMap<>();
116 protected Map<String,FieldType> fieldTypes = new HashMap<>();
117
118 protected List<SchemaField> fieldsWithDefaultValue = new ArrayList<>();
119 protected Collection<SchemaField> requiredFields = new HashSet<>();
120 protected volatile DynamicField[] dynamicFields;
121 public DynamicField[] getDynamicFields() { return dynamicFields; }
122
123 private Analyzer indexAnalyzer;
124 private Analyzer queryAnalyzer;
125
126 protected List<SchemaAware> schemaAware = new ArrayList<>();
127
128 protected String defaultSearchFieldName=null;
129 protected String queryParserDefaultOperator = "OR";
130 protected boolean isExplicitQueryParserDefaultOperator = false;
131
132
133 protected Map<String, List<CopyField>> copyFieldsMap = new HashMap<>();
134 public Map<String,List<CopyField>> getCopyFieldsMap() { return Collections.unmodifiableMap(copyFieldsMap); }
135
136 protected DynamicCopy[] dynamicCopyFields;
137 public DynamicCopy[] getDynamicCopyFields() { return dynamicCopyFields; }
138
139
140
141
142
143 protected Map<SchemaField, Integer> copyFieldTargetCounts = new HashMap<>();
144
145
146
147
148
149
150
151 public IndexSchema(SolrConfig solrConfig, String name, InputSource is) {
152 assert null != solrConfig : "SolrConfig should never be null";
153 assert null != name : "schema resource name should never be null";
154 assert null != is : "schema InputSource should never be null";
155
156 this.solrConfig = solrConfig;
157 this.resourceName = name;
158 loader = solrConfig.getResourceLoader();
159 try {
160 readSchema(is);
161 loader.inform(loader);
162 } catch (IOException e) {
163 throw new RuntimeException(e);
164 }
165 }
166
167
168
169
170 public SolrResourceLoader getResourceLoader() {
171 return loader;
172 }
173
174
175 public String getResourceName() {
176 return resourceName;
177 }
178
179
180 public void setResourceName(String resourceName) {
181 this.resourceName = resourceName;
182 }
183
184
185 public String getSchemaName() {
186 return name;
187 }
188
189
190 public Version getDefaultLuceneMatchVersion() {
191 return solrConfig.luceneMatchVersion;
192 }
193
194 public float getVersion() {
195 return version;
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 public Map<String,SchemaField> getFields() { return fields; }
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231 public Map<String,FieldType> getFieldTypes() { return fieldTypes; }
232
233
234
235
236 public List<SchemaField> getFieldsWithDefaultValue() { return fieldsWithDefaultValue; }
237
238
239
240
241
242 public Collection<SchemaField> getRequiredFields() { return requiredFields; }
243
244 protected Similarity similarity;
245
246
247
248
249 public Similarity getSimilarity() {
250 if (null == similarity) {
251 similarity = similarityFactory.getSimilarity();
252 }
253 return similarity;
254 }
255
256 protected SimilarityFactory similarityFactory;
257 protected boolean isExplicitSimilarity = false;
258
259
260
261 public SimilarityFactory getSimilarityFactory() { return similarityFactory; }
262
263
264
265
266
267
268
269
270
271 public Analyzer getIndexAnalyzer() { return indexAnalyzer; }
272
273
274
275
276
277
278
279
280
281 public Analyzer getQueryAnalyzer() { return queryAnalyzer; }
282
283
284
285
286
287
288
289 public String getDefaultSearchFieldName() {
290 return defaultSearchFieldName;
291 }
292
293
294
295
296 public String getQueryParserDefaultOperator() {
297 return queryParserDefaultOperator;
298 }
299
300 protected SchemaField uniqueKeyField;
301
302
303
304
305
306 public SchemaField getUniqueKeyField() { return uniqueKeyField; }
307
308 protected String uniqueKeyFieldName;
309 protected FieldType uniqueKeyFieldType;
310
311
312
313
314
315
316
317 public IndexableField getUniqueKeyField(org.apache.lucene.document.Document doc) {
318 return doc.getField(uniqueKeyFieldName);
319 }
320
321
322
323
324
325
326 public String printableUniqueKey(org.apache.lucene.document.Document doc) {
327 IndexableField f = doc.getField(uniqueKeyFieldName);
328 return f==null ? null : uniqueKeyFieldType.toExternal(f);
329 }
330
331 private SchemaField getIndexedField(String fname) {
332 SchemaField f = getFields().get(fname);
333 if (f==null) {
334 throw new RuntimeException("unknown field '" + fname + "'");
335 }
336 if (!f.indexed()) {
337 throw new RuntimeException("'"+fname+"' is not an indexed field:" + f);
338 }
339 return f;
340 }
341
342
343
344
345
346
347
348
349 public void refreshAnalyzers() {
350 indexAnalyzer = new SolrIndexAnalyzer();
351 queryAnalyzer = new SolrQueryAnalyzer();
352 }
353
354 public Map<String,UninvertingReader.Type> getUninversionMap(IndexReader reader) {
355 Map<String,UninvertingReader.Type> map = new HashMap<>();
356 for (FieldInfo f : MultiFields.getMergedFieldInfos(reader)) {
357 if (f.getDocValuesType() == DocValuesType.NONE && f.getIndexOptions() != IndexOptions.NONE) {
358 SchemaField sf = getFieldOrNull(f.name);
359 if (sf != null) {
360 UninvertingReader.Type type = sf.getType().getUninversionType(sf);
361 if (type != null) {
362 map.put(f.name, type);
363 }
364 }
365 }
366 }
367 return map;
368 }
369
370
371
372
373 void persist(Writer writer) throws IOException {
374 final SolrQueryResponse response = new SolrQueryResponse();
375 response.add(IndexSchema.SCHEMA, getNamedPropertyValues());
376 final NamedList args = new NamedList(Arrays.<Object>asList("indent", "on"));
377 final LocalSolrQueryRequest req = new LocalSolrQueryRequest(null, args);
378 final SchemaXmlWriter schemaXmlWriter = new SchemaXmlWriter(writer, req, response);
379 schemaXmlWriter.setEmitManagedSchemaDoNotEditWarning(true);
380 schemaXmlWriter.writeResponse();
381 schemaXmlWriter.close();
382 }
383
384 public boolean isMutable() {
385 return false;
386 }
387
388 private class SolrIndexAnalyzer extends DelegatingAnalyzerWrapper {
389 protected final HashMap<String, Analyzer> analyzers;
390
391 SolrIndexAnalyzer() {
392 super(PER_FIELD_REUSE_STRATEGY);
393 analyzers = analyzerCache();
394 }
395
396 protected HashMap<String, Analyzer> analyzerCache() {
397 HashMap<String, Analyzer> cache = new HashMap<>();
398 for (SchemaField f : getFields().values()) {
399 Analyzer analyzer = f.getType().getIndexAnalyzer();
400 cache.put(f.getName(), analyzer);
401 }
402 return cache;
403 }
404
405 @Override
406 protected Analyzer getWrappedAnalyzer(String fieldName) {
407 Analyzer analyzer = analyzers.get(fieldName);
408 return analyzer != null ? analyzer : getDynamicFieldType(fieldName).getIndexAnalyzer();
409 }
410
411 }
412
413 private class SolrQueryAnalyzer extends SolrIndexAnalyzer {
414 SolrQueryAnalyzer() {}
415
416 @Override
417 protected HashMap<String, Analyzer> analyzerCache() {
418 HashMap<String, Analyzer> cache = new HashMap<>();
419 for (SchemaField f : getFields().values()) {
420 Analyzer analyzer = f.getType().getQueryAnalyzer();
421 cache.put(f.getName(), analyzer);
422 }
423 return cache;
424 }
425
426 @Override
427 protected Analyzer getWrappedAnalyzer(String fieldName) {
428 Analyzer analyzer = analyzers.get(fieldName);
429 return analyzer != null ? analyzer : getDynamicFieldType(fieldName).getQueryAnalyzer();
430 }
431 }
432
433 protected void readSchema(InputSource is) {
434 try {
435
436
437 Config schemaConf = new Config(loader, SCHEMA, is, SLASH+SCHEMA+SLASH);
438 Document document = schemaConf.getDocument();
439 final XPath xpath = schemaConf.getXPath();
440 String expression = stepsToPath(SCHEMA, AT + NAME);
441 Node nd = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
442 StringBuilder sb = new StringBuilder();
443
444 sb.append("[");
445 if (loader.getCoreProperties() != null) {
446 sb.append(loader.getCoreProperties().getProperty(SOLR_CORE_NAME));
447 } else {
448 sb.append("null");
449 }
450 sb.append("] ");
451 if (nd==null) {
452 sb.append("schema has no name!");
453 log.warn(sb.toString());
454 } else {
455 name = nd.getNodeValue();
456 sb.append("Schema ");
457 sb.append(NAME);
458 sb.append("=");
459 sb.append(name);
460 log.info(sb.toString());
461 }
462
463
464 expression = stepsToPath(SCHEMA, AT + VERSION);
465 version = schemaConf.getFloat(expression, 1.0f);
466
467
468 final FieldTypePluginLoader typeLoader = new FieldTypePluginLoader(this, fieldTypes, schemaAware);
469 expression = getFieldTypeXPathExpressions();
470 NodeList nodes = (NodeList) xpath.evaluate(expression, document, XPathConstants.NODESET);
471 typeLoader.load(loader, nodes);
472
473
474 Map<String,Boolean> explicitRequiredProp = loadFields(document, xpath);
475
476 expression = stepsToPath(SCHEMA, SIMILARITY);
477 Node node = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
478 similarityFactory = readSimilarity(loader, node);
479 if (similarityFactory == null) {
480 similarityFactory = new ClassicSimilarityFactory();
481 final NamedList similarityParams = new NamedList();
482 Version luceneVersion = getDefaultLuceneMatchVersion();
483 if (!luceneVersion.onOrAfter(Version.LUCENE_4_7_0)) {
484 similarityParams.add(ClassicSimilarityFactory.DISCOUNT_OVERLAPS, false);
485 }
486 similarityFactory.init(SolrParams.toSolrParams(similarityParams));
487 } else {
488 isExplicitSimilarity = true;
489 }
490 if ( ! (similarityFactory instanceof SolrCoreAware)) {
491
492
493 for (FieldType ft : fieldTypes.values()) {
494 if (null != ft.getSimilarity()) {
495 String msg = "FieldType '" + ft.getTypeName()
496 + "' is configured with a similarity, but the global similarity does not support it: "
497 + similarityFactory.getClass();
498 log.error(msg);
499 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
500 }
501 }
502 }
503
504
505 expression = stepsToPath(SCHEMA, DEFAULT_SEARCH_FIELD, TEXT_FUNCTION);
506 node = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
507 if (node==null) {
508 log.debug("no default search field specified in schema.");
509 } else {
510 defaultSearchFieldName=node.getNodeValue().trim();
511
512 if (defaultSearchFieldName!=null) {
513 SchemaField defaultSearchField = getFields().get(defaultSearchFieldName);
514 if ((defaultSearchField == null) || !defaultSearchField.indexed()) {
515 String msg = "default search field '" + defaultSearchFieldName + "' not defined or not indexed" ;
516 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
517 }
518 }
519 log.info("default search field in schema is "+defaultSearchFieldName);
520 }
521
522
523 expression = stepsToPath(SCHEMA, SOLR_QUERY_PARSER, AT + DEFAULT_OPERATOR);
524 node = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
525 if (node==null) {
526 log.debug("using default query parser operator (OR)");
527 } else {
528 isExplicitQueryParserDefaultOperator = true;
529 queryParserDefaultOperator=node.getNodeValue().trim();
530 log.info("query parser default operator is "+queryParserDefaultOperator);
531 }
532
533
534 expression = stepsToPath(SCHEMA, UNIQUE_KEY, TEXT_FUNCTION);
535 node = (Node) xpath.evaluate(expression, document, XPathConstants.NODE);
536 if (node==null) {
537 log.warn("no " + UNIQUE_KEY + " specified in schema.");
538 } else {
539 uniqueKeyField=getIndexedField(node.getNodeValue().trim());
540 if (null != uniqueKeyField.getDefaultValue()) {
541 String msg = UNIQUE_KEY + " field ("+uniqueKeyFieldName+
542 ") can not be configured with a default value ("+
543 uniqueKeyField.getDefaultValue()+")";
544 log.error(msg);
545 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
546 }
547
548 if (!uniqueKeyField.stored()) {
549 log.warn(UNIQUE_KEY + " is not stored - distributed search and MoreLikeThis will not work");
550 }
551 if (uniqueKeyField.multiValued()) {
552 String msg = UNIQUE_KEY + " field ("+uniqueKeyFieldName+
553 ") can not be configured to be multivalued";
554 log.error(msg);
555 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
556 }
557 uniqueKeyFieldName=uniqueKeyField.getName();
558 uniqueKeyFieldType=uniqueKeyField.getType();
559 log.info("unique key field: "+uniqueKeyFieldName);
560
561
562 if( Boolean.FALSE != explicitRequiredProp.get( uniqueKeyFieldName ) ) {
563 uniqueKeyField.required = true;
564 requiredFields.add(uniqueKeyField);
565 }
566 }
567
568
569
570
571
572 dynamicCopyFields = new DynamicCopy[] {};
573 loadCopyFields(document, xpath);
574
575 postReadInform();
576
577 } catch (SolrException e) {
578 throw new SolrException(ErrorCode.getErrorCode(e.code()),
579 "Can't load schema " + loader.resourceLocation(resourceName) + ": " + e.getMessage(), e);
580 } catch(Exception e) {
581
582 throw new SolrException(ErrorCode.SERVER_ERROR,
583 "Can't load schema " + loader.resourceLocation(resourceName) + ": " + e.getMessage(), e);
584 }
585
586
587 refreshAnalyzers();
588 }
589
590 protected void postReadInform() {
591
592 for (SchemaAware aware : schemaAware) {
593 aware.inform(this);
594 }
595 }
596
597
598
599
600
601
602 protected synchronized Map<String,Boolean> loadFields(Document document, XPath xpath) throws XPathExpressionException {
603
604 Map<String,Boolean> explicitRequiredProp = new HashMap<>();
605
606 ArrayList<DynamicField> dFields = new ArrayList<>();
607
608
609 String expression = stepsToPath(SCHEMA, FIELD)
610 + XPATH_OR + stepsToPath(SCHEMA, DYNAMIC_FIELD)
611 + XPATH_OR + stepsToPath(SCHEMA, FIELDS, FIELD)
612 + XPATH_OR + stepsToPath(SCHEMA, FIELDS, DYNAMIC_FIELD);
613
614 NodeList nodes = (NodeList)xpath.evaluate(expression, document, XPathConstants.NODESET);
615
616 for (int i=0; i<nodes.getLength(); i++) {
617 Node node = nodes.item(i);
618
619 NamedNodeMap attrs = node.getAttributes();
620
621 String name = DOMUtil.getAttr(attrs, NAME, "field definition");
622 log.trace("reading field def "+name);
623 String type = DOMUtil.getAttr(attrs, TYPE, "field " + name);
624
625 FieldType ft = fieldTypes.get(type);
626 if (ft==null) {
627 throw new SolrException
628 (ErrorCode.BAD_REQUEST, "Unknown " + FIELD_TYPE + " '" + type + "' specified on field " + name);
629 }
630
631 Map<String,String> args = DOMUtil.toMapExcept(attrs, NAME, TYPE);
632 if (null != args.get(REQUIRED)) {
633 explicitRequiredProp.put(name, Boolean.valueOf(args.get(REQUIRED)));
634 }
635
636 SchemaField f = SchemaField.create(name,ft,args);
637
638 if (node.getNodeName().equals(FIELD)) {
639 SchemaField old = fields.put(f.getName(),f);
640 if( old != null ) {
641 String msg = "[schema.xml] Duplicate field definition for '"
642 + f.getName() + "' [[["+old.toString()+"]]] and [[["+f.toString()+"]]]";
643 throw new SolrException(ErrorCode.SERVER_ERROR, msg );
644 }
645 log.debug("field defined: " + f);
646 if( f.getDefaultValue() != null ) {
647 log.debug(name+" contains default value: " + f.getDefaultValue());
648 fieldsWithDefaultValue.add( f );
649 }
650 if (f.isRequired()) {
651 log.debug(name+" is required in this schema");
652 requiredFields.add(f);
653 }
654 } else if (node.getNodeName().equals(DYNAMIC_FIELD)) {
655 if (isValidDynamicField(dFields, f)) {
656 addDynamicFieldNoDupCheck(dFields, f);
657 }
658 } else {
659
660 throw new RuntimeException("Unknown field type");
661 }
662 }
663
664
665
666
667 requiredFields.addAll(fieldsWithDefaultValue);
668
669 dynamicFields = dynamicFieldListToSortedArray(dFields);
670
671 return explicitRequiredProp;
672 }
673
674
675
676
677 protected static DynamicField[] dynamicFieldListToSortedArray(List<DynamicField> dynamicFieldList) {
678
679
680
681 DynamicField[] dFields = dynamicFieldList.toArray(new DynamicField[dynamicFieldList.size()]);
682 Arrays.sort(dFields);
683
684 log.trace("Dynamic Field Ordering:" + Arrays.toString(dFields));
685
686 return dFields;
687 }
688
689
690
691
692 protected synchronized void loadCopyFields(Document document, XPath xpath) throws XPathExpressionException {
693 String expression = "//" + COPY_FIELD;
694 NodeList nodes = (NodeList)xpath.evaluate(expression, document, XPathConstants.NODESET);
695
696 for (int i=0; i<nodes.getLength(); i++) {
697 Node node = nodes.item(i);
698 NamedNodeMap attrs = node.getAttributes();
699
700 String source = DOMUtil.getAttr(attrs, SOURCE, COPY_FIELD + " definition");
701 String dest = DOMUtil.getAttr(attrs, DESTINATION, COPY_FIELD + " definition");
702 String maxChars = DOMUtil.getAttr(attrs, MAX_CHARS);
703
704 int maxCharsInt = CopyField.UNLIMITED;
705 if (maxChars != null) {
706 try {
707 maxCharsInt = Integer.parseInt(maxChars);
708 } catch (NumberFormatException e) {
709 log.warn("Couldn't parse " + MAX_CHARS + " attribute for " + COPY_FIELD + " from "
710 + source + " to " + dest + " as integer. The whole field will be copied.");
711 }
712 }
713
714 if (dest.equals(uniqueKeyFieldName)) {
715 String msg = UNIQUE_KEY + " field ("+uniqueKeyFieldName+
716 ") can not be the " + DESTINATION + " of a " + COPY_FIELD + "(" + SOURCE + "=" +source+")";
717 log.error(msg);
718 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
719 }
720
721 registerCopyField(source, dest, maxCharsInt);
722 }
723
724 for (Map.Entry<SchemaField, Integer> entry : copyFieldTargetCounts.entrySet()) {
725 if (entry.getValue() > 1 && !entry.getKey().multiValued()) {
726 log.warn("Field " + entry.getKey().name + " is not multivalued "+
727 "and destination for multiple " + COPY_FIELDS + " ("+
728 entry.getValue()+")");
729 }
730 }
731 }
732
733
734
735
736
737
738 private String stepsToPath(String... steps) {
739 StringBuilder builder = new StringBuilder();
740 for (String step : steps) { builder.append(SLASH).append(step); }
741 return builder.toString();
742 }
743
744
745 protected static boolean isValidFieldGlob(String name) {
746 if (name.startsWith("*") || name.endsWith("*")) {
747 int count = 0;
748 for (int pos = 0 ; pos < name.length() && -1 != (pos = name.indexOf('*', pos)) ; ++pos) ++count;
749 if (1 == count) return true;
750 }
751 return false;
752 }
753
754 protected boolean isValidDynamicField(List<DynamicField> dFields, SchemaField f) {
755 String glob = f.getName();
756 if (f.getDefaultValue() != null) {
757 throw new SolrException(ErrorCode.SERVER_ERROR,
758 DYNAMIC_FIELD + " can not have a default value: " + glob);
759 }
760 if (f.isRequired()) {
761 throw new SolrException(ErrorCode.SERVER_ERROR,
762 DYNAMIC_FIELD + " can not be required: " + glob);
763 }
764 if ( ! isValidFieldGlob(glob)) {
765 String msg = "Dynamic field name '" + glob
766 + "' should have either a leading or a trailing asterisk, and no others.";
767 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
768 }
769 if (isDuplicateDynField(dFields, f)) {
770 String msg = "[schema.xml] Duplicate DynamicField definition for '" + glob + "'";
771 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
772 }
773 return true;
774 }
775
776
777
778
779
780
781 public void registerDynamicFields(SchemaField... fields) {
782 List<DynamicField> dynFields = new ArrayList<>(Arrays.asList(dynamicFields));
783 for (SchemaField field : fields) {
784 if (isDuplicateDynField(dynFields, field)) {
785 log.debug("dynamic field already exists: dynamic field: [" + field.getName() + "]");
786 } else {
787 log.debug("dynamic field creation for schema field: " + field.getName());
788 addDynamicFieldNoDupCheck(dynFields, field);
789 }
790 }
791 dynamicFields = dynamicFieldListToSortedArray(dynFields);
792 }
793
794 private void addDynamicFieldNoDupCheck(List<DynamicField> dFields, SchemaField f) {
795 dFields.add(new DynamicField(f));
796 log.debug("dynamic field defined: " + f);
797 }
798
799 protected boolean isDuplicateDynField(List<DynamicField> dFields, SchemaField f) {
800 for (DynamicField df : dFields) {
801 if (df.getRegex().equals(f.name)) return true;
802 }
803 return false;
804 }
805
806 public void registerCopyField( String source, String dest ) {
807 registerCopyField(source, dest, CopyField.UNLIMITED);
808 }
809
810
811
812
813
814
815
816
817
818
819 public void registerCopyField(String source, String dest, int maxChars) {
820 log.debug(COPY_FIELD + " " + SOURCE + "='" + source + "' " + DESTINATION + "='" + dest
821 + "' " + MAX_CHARS + "=" + maxChars);
822
823 DynamicField destDynamicField = null;
824 SchemaField destSchemaField = fields.get(dest);
825 SchemaField sourceSchemaField = fields.get(source);
826
827 DynamicField sourceDynamicBase = null;
828 DynamicField destDynamicBase = null;
829
830 boolean sourceIsDynamicFieldReference = false;
831 boolean sourceIsExplicitFieldGlob = false;
832
833
834 final String invalidGlobMessage = "is an invalid glob: either it contains more than one asterisk,"
835 + " or the asterisk occurs neither at the start nor at the end.";
836 final boolean sourceIsGlob = isValidFieldGlob(source);
837 if (source.contains("*") && ! sourceIsGlob) {
838 String msg = "copyField source :'" + source + "' " + invalidGlobMessage;
839 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
840 }
841 if (dest.contains("*") && ! isValidFieldGlob(dest)) {
842 String msg = "copyField dest :'" + dest + "' " + invalidGlobMessage;
843 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
844 }
845
846 if (null == sourceSchemaField && sourceIsGlob) {
847 Pattern pattern = Pattern.compile(source.replace("*", ".*"));
848 for (String field : fields.keySet()) {
849 if (pattern.matcher(field).matches()) {
850 sourceIsExplicitFieldGlob = true;
851 break;
852 }
853 }
854 }
855
856 if (null == destSchemaField || (null == sourceSchemaField && ! sourceIsExplicitFieldGlob)) {
857
858 for (DynamicField dynamicField : dynamicFields) {
859 if (null == sourceSchemaField && ! sourceIsDynamicFieldReference && ! sourceIsExplicitFieldGlob) {
860 if (dynamicField.matches(source)) {
861 sourceIsDynamicFieldReference = true;
862 if ( ! source.equals(dynamicField.getRegex())) {
863 sourceDynamicBase = dynamicField;
864 }
865 }
866 }
867 if (null == destSchemaField) {
868 if (dest.equals(dynamicField.getRegex())) {
869 destDynamicField = dynamicField;
870 destSchemaField = dynamicField.prototype;
871 } else if (dynamicField.matches(dest)) {
872 destSchemaField = dynamicField.makeSchemaField(dest);
873 destDynamicField = new DynamicField(destSchemaField);
874 destDynamicBase = dynamicField;
875 }
876 }
877 if (null != destSchemaField
878 && (null != sourceSchemaField || sourceIsDynamicFieldReference || sourceIsExplicitFieldGlob)) {
879 break;
880 }
881 }
882 }
883 if (null == sourceSchemaField && ! sourceIsGlob && ! sourceIsDynamicFieldReference) {
884 String msg = "copyField source :'" + source + "' is not a glob and doesn't match any explicit field or dynamicField.";
885 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
886 }
887 if (null == destSchemaField) {
888 String msg = "copyField dest :'" + dest + "' is not an explicit field and doesn't match a dynamicField.";
889 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
890 }
891 if (sourceIsGlob) {
892 if (null != destDynamicField) {
893 registerDynamicCopyField(new DynamicCopy(source, destDynamicField, maxChars, sourceDynamicBase, destDynamicBase));
894 incrementCopyFieldTargetCount(destSchemaField);
895 } else {
896 destDynamicField = new DynamicField(destSchemaField);
897 registerDynamicCopyField(new DynamicCopy(source, destDynamicField, maxChars, sourceDynamicBase, null));
898 incrementCopyFieldTargetCount(destSchemaField);
899 }
900 } else if (sourceIsDynamicFieldReference) {
901 if (null != destDynamicField) {
902 registerDynamicCopyField(new DynamicCopy(source, destDynamicField, maxChars, sourceDynamicBase, destDynamicBase));
903 incrementCopyFieldTargetCount(destSchemaField);
904 } else {
905 sourceSchemaField = getField(source);
906 registerExplicitSrcAndDestFields(source, maxChars, destSchemaField, sourceSchemaField);
907 }
908 } else {
909 if (null != destDynamicField) {
910 if (destDynamicField.pattern instanceof DynamicReplacement.DynamicPattern.NameEquals) {
911
912 registerDynamicCopyField(new DynamicCopy(source, destDynamicField, maxChars, sourceDynamicBase, destDynamicBase));
913 incrementCopyFieldTargetCount(destSchemaField);
914 } else {
915 String msg = "copyField only supports a dynamic destination with an asterisk "
916 + "if the source also has an asterisk";
917 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
918 }
919 } else {
920 registerExplicitSrcAndDestFields(source, maxChars, destSchemaField, sourceSchemaField);
921 }
922 }
923 }
924
925 protected void registerExplicitSrcAndDestFields(String source, int maxChars, SchemaField destSchemaField, SchemaField sourceSchemaField) {
926 List<CopyField> copyFieldList = copyFieldsMap.get(source);
927 if (copyFieldList == null) {
928 copyFieldList = new ArrayList<>();
929 copyFieldsMap.put(source, copyFieldList);
930 }
931 copyFieldList.add(new CopyField(sourceSchemaField, destSchemaField, maxChars));
932 incrementCopyFieldTargetCount(destSchemaField);
933 }
934
935 private void incrementCopyFieldTargetCount(SchemaField dest) {
936 copyFieldTargetCounts.put(dest, copyFieldTargetCounts.containsKey(dest) ? copyFieldTargetCounts.get(dest) + 1 : 1);
937 }
938
939 private void registerDynamicCopyField( DynamicCopy dcopy ) {
940 if( dynamicCopyFields == null ) {
941 dynamicCopyFields = new DynamicCopy[] {dcopy};
942 }
943 else {
944 DynamicCopy[] temp = new DynamicCopy[dynamicCopyFields.length+1];
945 System.arraycopy(dynamicCopyFields,0,temp,0,dynamicCopyFields.length);
946 temp[temp.length -1] = dcopy;
947 dynamicCopyFields = temp;
948 }
949 log.trace("Dynamic Copy Field:" + dcopy);
950 }
951
952 static SimilarityFactory readSimilarity(SolrResourceLoader loader, Node node) {
953 if (node==null) {
954 return null;
955 } else {
956 SimilarityFactory similarityFactory;
957 final String classArg = ((Element) node).getAttribute(SimilarityFactory.CLASS_NAME);
958 final Object obj = loader.newInstance(classArg, Object.class, "search.similarities.");
959 if (obj instanceof SimilarityFactory) {
960
961 final NamedList<Object> namedList = DOMUtil.childNodesToNamedList(node);
962 namedList.add(SimilarityFactory.CLASS_NAME, classArg);
963 SolrParams params = SolrParams.toSolrParams(namedList);
964 similarityFactory = (SimilarityFactory)obj;
965 similarityFactory.init(params);
966 } else {
967
968 similarityFactory = new SimilarityFactory() {
969 @Override
970 public Similarity getSimilarity() {
971 return (Similarity) obj;
972 }
973 };
974 }
975 return similarityFactory;
976 }
977 }
978
979
980 public static abstract class DynamicReplacement implements Comparable<DynamicReplacement> {
981 abstract protected static class DynamicPattern {
982 protected final String regex;
983 protected final String fixedStr;
984
985 protected DynamicPattern(String regex, String fixedStr) { this.regex = regex; this.fixedStr = fixedStr; }
986
987 static DynamicPattern createPattern(String regex) {
988 if (regex.startsWith("*")) { return new NameEndsWith(regex); }
989 else if (regex.endsWith("*")) { return new NameStartsWith(regex); }
990 else { return new NameEquals(regex);
991 }
992 }
993
994
995 abstract boolean matches(String name);
996
997
998 abstract String remainder(String name);
999
1000
1001 abstract String subst(String replacement);
1002
1003
1004 public int length() { return regex.length(); }
1005
1006 private static class NameStartsWith extends DynamicPattern {
1007 NameStartsWith(String regex) { super(regex, regex.substring(0, regex.length() - 1)); }
1008 boolean matches(String name) { return name.startsWith(fixedStr); }
1009 String remainder(String name) { return name.substring(fixedStr.length()); }
1010 String subst(String replacement) { return fixedStr + replacement; }
1011 }
1012 private static class NameEndsWith extends DynamicPattern {
1013 NameEndsWith(String regex) { super(regex, regex.substring(1)); }
1014 boolean matches(String name) { return name.endsWith(fixedStr); }
1015 String remainder(String name) { return name.substring(0, name.length() - fixedStr.length()); }
1016 String subst(String replacement) { return replacement + fixedStr; }
1017 }
1018 private static class NameEquals extends DynamicPattern {
1019 NameEquals(String regex) { super(regex, regex); }
1020 boolean matches(String name) { return regex.equals(name); }
1021 String remainder(String name) { return ""; }
1022 String subst(String replacement) { return fixedStr; }
1023 }
1024 }
1025
1026 protected DynamicPattern pattern;
1027
1028 public boolean matches(String name) { return pattern.matches(name); }
1029
1030 protected DynamicReplacement(String regex) {
1031 pattern = DynamicPattern.createPattern(regex);
1032 }
1033
1034
1035
1036
1037
1038
1039
1040
1041 @Override
1042 public int compareTo(DynamicReplacement other) {
1043 return other.pattern.length() - pattern.length();
1044 }
1045
1046
1047 public String getRegex() {
1048 return pattern.regex;
1049 }
1050 }
1051
1052
1053 public final static class DynamicField extends DynamicReplacement {
1054 private final SchemaField prototype;
1055 public SchemaField getPrototype() { return prototype; }
1056
1057 DynamicField(SchemaField prototype) {
1058 super(prototype.name);
1059 this.prototype=prototype;
1060 }
1061
1062 SchemaField makeSchemaField(String name) {
1063
1064
1065
1066
1067
1068 return new SchemaField(prototype, name);
1069 }
1070
1071 @Override
1072 public String toString() {
1073 return prototype.toString();
1074 }
1075 }
1076
1077 public static class DynamicCopy extends DynamicReplacement {
1078 private final DynamicField destination;
1079
1080 private final int maxChars;
1081 public int getMaxChars() { return maxChars; }
1082
1083 final DynamicField sourceDynamicBase;
1084 public DynamicField getSourceDynamicBase() { return sourceDynamicBase; }
1085
1086 final DynamicField destDynamicBase;
1087 public DynamicField getDestDynamicBase() { return destDynamicBase; }
1088
1089 DynamicCopy(String sourceRegex, DynamicField destination, int maxChars,
1090 DynamicField sourceDynamicBase, DynamicField destDynamicBase) {
1091 super(sourceRegex);
1092 this.destination = destination;
1093 this.maxChars = maxChars;
1094 this.sourceDynamicBase = sourceDynamicBase;
1095 this.destDynamicBase = destDynamicBase;
1096 }
1097
1098 public DynamicField getDestination() { return destination; }
1099
1100 public String getDestFieldName() { return destination.getRegex(); }
1101
1102
1103
1104
1105
1106
1107 public SchemaField getTargetField(String sourceField) {
1108 String remainder = pattern.remainder(sourceField);
1109 String targetFieldName = destination.pattern.subst(remainder);
1110 return destination.makeSchemaField(targetFieldName);
1111 }
1112
1113
1114 @Override
1115 public String toString() {
1116 return destination.prototype.toString();
1117 }
1118 }
1119
1120 public SchemaField[] getDynamicFieldPrototypes() {
1121 SchemaField[] df = new SchemaField[dynamicFields.length];
1122 for (int i=0;i<dynamicFields.length;i++) {
1123 df[i] = dynamicFields[i].prototype;
1124 }
1125 return df;
1126 }
1127
1128 public String getDynamicPattern(String fieldName) {
1129 for (DynamicField df : dynamicFields) {
1130 if (df.matches(fieldName)) return df.getRegex();
1131 }
1132 return null;
1133 }
1134
1135
1136
1137
1138
1139
1140
1141 public boolean hasExplicitField(String fieldName) {
1142 if (fields.containsKey(fieldName)) {
1143 return true;
1144 }
1145
1146 for (DynamicField df : dynamicFields) {
1147 if (fieldName.equals(df.getRegex())) return true;
1148 }
1149
1150 return false;
1151 }
1152
1153
1154
1155
1156
1157 public boolean isDynamicField(String fieldName) {
1158 if(fields.containsKey(fieldName)) {
1159 return false;
1160 }
1161
1162 for (DynamicField df : dynamicFields) {
1163 if (df.matches(fieldName)) return true;
1164 }
1165
1166 return false;
1167 }
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179 public SchemaField getFieldOrNull(String fieldName) {
1180 SchemaField f = fields.get(fieldName);
1181 if (f != null) return f;
1182
1183 for (DynamicField df : dynamicFields) {
1184 if (df.matches(fieldName)) return df.makeSchemaField(fieldName);
1185 }
1186
1187 return f;
1188 }
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200 public SchemaField getField(String fieldName) {
1201 SchemaField f = getFieldOrNull(fieldName);
1202 if (f != null) return f;
1203
1204
1205
1206
1207
1208
1209
1210 throw new SolrException(ErrorCode.BAD_REQUEST,"undefined field: \""+fieldName+"\"");
1211 }
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227 public FieldType getFieldType(String fieldName) {
1228 SchemaField f = fields.get(fieldName);
1229 if (f != null) return f.getType();
1230
1231 return getDynamicFieldType(fieldName);
1232 }
1233
1234
1235
1236
1237
1238
1239
1240 public FieldType getFieldTypeByName(String fieldTypeName){
1241 return fieldTypes.get(fieldTypeName);
1242 }
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258 public FieldType getFieldTypeNoEx(String fieldName) {
1259 SchemaField f = fields.get(fieldName);
1260 if (f != null) return f.getType();
1261 return dynFieldType(fieldName);
1262 }
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275 public FieldType getDynamicFieldType(String fieldName) {
1276 for (DynamicField df : dynamicFields) {
1277 if (df.matches(fieldName)) return df.prototype.getType();
1278 }
1279 throw new SolrException(ErrorCode.BAD_REQUEST,"undefined field "+fieldName);
1280 }
1281
1282 private FieldType dynFieldType(String fieldName) {
1283 for (DynamicField df : dynamicFields) {
1284 if (df.matches(fieldName)) return df.prototype.getType();
1285 }
1286 return null;
1287 }
1288
1289
1290
1291
1292
1293
1294
1295 public List<String> getCopySources(String destField) {
1296 SchemaField f = getField(destField);
1297 if (!isCopyFieldTarget(f)) {
1298 return Collections.emptyList();
1299 }
1300 List<String> fieldNames = new ArrayList<>();
1301 for (Map.Entry<String, List<CopyField>> cfs : copyFieldsMap.entrySet()) {
1302 for (CopyField copyField : cfs.getValue()) {
1303 if (copyField.getDestination().getName().equals(destField)) {
1304 fieldNames.add(copyField.getSource().getName());
1305 }
1306 }
1307 }
1308 if (null != dynamicCopyFields) {
1309 for (DynamicCopy dynamicCopy : dynamicCopyFields) {
1310 if (dynamicCopy.getDestFieldName().equals(destField)) {
1311 fieldNames.add(dynamicCopy.getRegex());
1312 }
1313 }
1314 }
1315 return fieldNames;
1316 }
1317
1318
1319
1320
1321
1322
1323
1324
1325 public List<CopyField> getCopyFieldsList(final String sourceField){
1326 final List<CopyField> result = new ArrayList<>();
1327 if (null != dynamicCopyFields) {
1328 for (DynamicCopy dynamicCopy : dynamicCopyFields) {
1329 if (dynamicCopy.matches(sourceField)) {
1330 result.add(new CopyField(getField(sourceField), dynamicCopy.getTargetField(sourceField), dynamicCopy.maxChars));
1331 }
1332 }
1333 }
1334 List<CopyField> fixedCopyFields = copyFieldsMap.get(sourceField);
1335 if (null != fixedCopyFields) {
1336 result.addAll(fixedCopyFields);
1337 }
1338
1339 return result;
1340 }
1341
1342
1343
1344
1345
1346
1347 public boolean isCopyFieldTarget( SchemaField f ) {
1348 return copyFieldTargetCounts.containsKey( f );
1349 }
1350
1351
1352
1353
1354 public SimpleOrderedMap<Object> getNamedPropertyValues() {
1355 SimpleOrderedMap<Object> topLevel = new SimpleOrderedMap<>();
1356 topLevel.add(NAME, getSchemaName());
1357 topLevel.add(VERSION, getVersion());
1358 if (null != uniqueKeyFieldName) {
1359 topLevel.add(UNIQUE_KEY, uniqueKeyFieldName);
1360 }
1361 if (null != defaultSearchFieldName) {
1362 topLevel.add(DEFAULT_SEARCH_FIELD, defaultSearchFieldName);
1363 }
1364 if (isExplicitQueryParserDefaultOperator) {
1365 SimpleOrderedMap<Object> solrQueryParserProperties = new SimpleOrderedMap<>();
1366 solrQueryParserProperties.add(DEFAULT_OPERATOR, queryParserDefaultOperator);
1367 topLevel.add(SOLR_QUERY_PARSER, solrQueryParserProperties);
1368 }
1369 if (isExplicitSimilarity) {
1370 topLevel.add(SIMILARITY, similarityFactory.getNamedPropertyValues());
1371 }
1372 List<SimpleOrderedMap<Object>> fieldTypeProperties = new ArrayList<>();
1373 SortedMap<String,FieldType> sortedFieldTypes = new TreeMap<>(fieldTypes);
1374 for (FieldType fieldType : sortedFieldTypes.values()) {
1375 fieldTypeProperties.add(fieldType.getNamedPropertyValues(false));
1376 }
1377 topLevel.add(FIELD_TYPES, fieldTypeProperties);
1378 List<SimpleOrderedMap<Object>> fieldProperties = new ArrayList<>();
1379 SortedSet<String> fieldNames = new TreeSet<>(fields.keySet());
1380 for (String fieldName : fieldNames) {
1381 fieldProperties.add(fields.get(fieldName).getNamedPropertyValues(false));
1382 }
1383 topLevel.add(FIELDS, fieldProperties);
1384 List<SimpleOrderedMap<Object>> dynamicFieldProperties = new ArrayList<>();
1385 for (IndexSchema.DynamicField dynamicField : dynamicFields) {
1386 if ( ! dynamicField.getRegex().startsWith(INTERNAL_POLY_FIELD_PREFIX)) {
1387 dynamicFieldProperties.add(dynamicField.getPrototype().getNamedPropertyValues(false));
1388 }
1389 }
1390 topLevel.add(DYNAMIC_FIELDS, dynamicFieldProperties);
1391 topLevel.add(COPY_FIELDS, getCopyFieldProperties(false, null, null));
1392 return topLevel;
1393 }
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407 public List<SimpleOrderedMap<Object>> getCopyFieldProperties
1408 (boolean showDetails, Set<String> requestedSourceFields, Set<String> requestedDestinationFields) {
1409 List<SimpleOrderedMap<Object>> copyFieldProperties = new ArrayList<>();
1410 SortedMap<String,List<CopyField>> sortedCopyFields = new TreeMap<>(copyFieldsMap);
1411 for (List<CopyField> copyFields : sortedCopyFields.values()) {
1412 copyFields = new ArrayList<>(copyFields);
1413 Collections.sort(copyFields, new Comparator<CopyField>() {
1414 @Override
1415 public int compare(CopyField cf1, CopyField cf2) {
1416
1417 return cf1.getDestination().getName().compareTo(cf2.getDestination().getName());
1418 }
1419 });
1420 for (CopyField copyField : copyFields) {
1421 final String source = copyField.getSource().getName();
1422 final String destination = copyField.getDestination().getName();
1423 if ( (null == requestedSourceFields || requestedSourceFields.contains(source))
1424 && (null == requestedDestinationFields || requestedDestinationFields.contains(destination))) {
1425 SimpleOrderedMap<Object> props = new SimpleOrderedMap<>();
1426 props.add(SOURCE, source);
1427 props.add(DESTINATION, destination);
1428 if (0 != copyField.getMaxChars()) {
1429 props.add(MAX_CHARS, copyField.getMaxChars());
1430 }
1431 copyFieldProperties.add(props);
1432 }
1433 }
1434 }
1435 if (null != dynamicCopyFields) {
1436 for (IndexSchema.DynamicCopy dynamicCopy : dynamicCopyFields) {
1437 final String source = dynamicCopy.getRegex();
1438 final String destination = dynamicCopy.getDestFieldName();
1439 if ((null == requestedSourceFields || requestedSourceFields.contains(source))
1440 && (null == requestedDestinationFields || requestedDestinationFields.contains(destination))) {
1441 SimpleOrderedMap<Object> dynamicCopyProps = new SimpleOrderedMap<>();
1442
1443 dynamicCopyProps.add(SOURCE, dynamicCopy.getRegex());
1444 if (showDetails) {
1445 IndexSchema.DynamicField sourceDynamicBase = dynamicCopy.getSourceDynamicBase();
1446 if (null != sourceDynamicBase) {
1447 dynamicCopyProps.add(SOURCE_DYNAMIC_BASE, sourceDynamicBase.getRegex());
1448 } else if (source.contains("*")) {
1449 List<String> sourceExplicitFields = new ArrayList<>();
1450 Pattern pattern = Pattern.compile(source.replace("*", ".*"));
1451 for (String field : fields.keySet()) {
1452 if (pattern.matcher(field).matches()) {
1453 sourceExplicitFields.add(field);
1454 }
1455 }
1456 if (sourceExplicitFields.size() > 0) {
1457 Collections.sort(sourceExplicitFields);
1458 dynamicCopyProps.add(SOURCE_EXPLICIT_FIELDS, sourceExplicitFields);
1459 }
1460 }
1461 }
1462
1463 dynamicCopyProps.add(DESTINATION, dynamicCopy.getDestFieldName());
1464 if (showDetails) {
1465 IndexSchema.DynamicField destDynamicBase = dynamicCopy.getDestDynamicBase();
1466 if (null != destDynamicBase) {
1467 dynamicCopyProps.add(DESTINATION_DYNAMIC_BASE, destDynamicBase.getRegex());
1468 }
1469 }
1470
1471 if (0 != dynamicCopy.getMaxChars()) {
1472 dynamicCopyProps.add(MAX_CHARS, dynamicCopy.getMaxChars());
1473 }
1474
1475 copyFieldProperties.add(dynamicCopyProps);
1476 }
1477 }
1478 }
1479 return copyFieldProperties;
1480 }
1481
1482 protected IndexSchema(final SolrConfig solrConfig, final SolrResourceLoader loader) {
1483 this.solrConfig = solrConfig;
1484 this.loader = loader;
1485 }
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497 public IndexSchema addField(SchemaField newField, boolean persist) {
1498 return addFields(Collections.singletonList(newField), Collections.EMPTY_MAP, persist );
1499 }
1500
1501 public IndexSchema addField(SchemaField newField) {
1502 return addField(newField, true);
1503 }
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515 public IndexSchema addField(SchemaField newField, Collection<String> copyFieldNames) {
1516 return addFields(singletonList(newField), singletonMap(newField.getName(), copyFieldNames), true);
1517 }
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528 public IndexSchema addFields(Collection<SchemaField> newFields) {
1529 return addFields(newFields, Collections.<String, Collection<String>>emptyMap(), true);
1530 }
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543 public IndexSchema addFields(Collection<SchemaField> newFields, Map<String, Collection<String>> copyFieldNames, boolean persist) {
1544 String msg = "This IndexSchema is not mutable.";
1545 log.error(msg);
1546 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1547 }
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561 public IndexSchema deleteFields(Collection<String> names) {
1562 String msg = "This IndexSchema is not mutable.";
1563 log.error(msg);
1564 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1565 }
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582 public IndexSchema replaceField(String fieldName, FieldType replacementFieldType, Map<String,?> replacementArgs) {
1583 String msg = "This IndexSchema is not mutable.";
1584 log.error(msg);
1585 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1586 }
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599 public IndexSchema addDynamicFields
1600 (Collection<SchemaField> newDynamicFields,
1601 Map<String, Collection<String>> copyFieldNames,
1602 boolean persist) {
1603 String msg = "This IndexSchema is not mutable.";
1604 log.error(msg);
1605 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1606 }
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619 public IndexSchema deleteDynamicFields(Collection<String> fieldNamePatterns) {
1620 String msg = "This IndexSchema is not mutable.";
1621 log.error(msg);
1622 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1623 }
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640 public ManagedIndexSchema replaceDynamicField
1641 (String fieldNamePattern, FieldType replacementFieldType, Map<String,?> replacementArgs) {
1642 String msg = "This IndexSchema is not mutable.";
1643 log.error(msg);
1644 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1645 }
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658 public IndexSchema addCopyFields(Map<String, Collection<String>> copyFields, boolean persist) {
1659 String msg = "This IndexSchema is not mutable.";
1660 log.error(msg);
1661 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1662 }
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677 public IndexSchema addCopyFields(String source, Collection<String> destinations, int maxChars) {
1678 String msg = "This IndexSchema is not mutable.";
1679 log.error(msg);
1680 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1681 }
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695 public IndexSchema deleteCopyFields(Map<String, Collection<String>> copyFields) {
1696 String msg = "This IndexSchema is not mutable.";
1697 log.error(msg);
1698 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1699 }
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714 public SchemaField newField(String fieldName, String fieldType, Map<String,?> options) {
1715 String msg = "This IndexSchema is not mutable.";
1716 log.error(msg);
1717 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1718 }
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732 public SchemaField newDynamicField(String fieldNamePattern, String fieldType, Map<String,?> options) {
1733 String msg = "This IndexSchema is not mutable.";
1734 log.error(msg);
1735 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1736 }
1737
1738
1739
1740
1741
1742
1743
1744 public Object getSchemaUpdateLock() {
1745 String msg = "This IndexSchema is not mutable.";
1746 log.error(msg);
1747 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1748 }
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759 public IndexSchema addFieldType(FieldType fieldType) {
1760 String msg = "This IndexSchema is not mutable.";
1761 log.error(msg);
1762 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1763 }
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775 public IndexSchema addFieldTypes(List<FieldType> fieldTypeList, boolean persist) {
1776 String msg = "This IndexSchema is not mutable.";
1777 log.error(msg);
1778 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1779 }
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791 public IndexSchema deleteFieldTypes(Collection<String> names) {
1792 String msg = "This IndexSchema is not mutable.";
1793 log.error(msg);
1794 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1795 }
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812 public IndexSchema replaceFieldType(String typeName, String replacementClassName, Map<String,Object> replacementArgs) {
1813 String msg = "This IndexSchema is not mutable.";
1814 log.error(msg);
1815 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1816 }
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829 public FieldType newFieldType(String typeName, String className, Map<String,?> options) {
1830 String msg = "This IndexSchema is not mutable.";
1831 log.error(msg);
1832 throw new SolrException(ErrorCode.SERVER_ERROR, msg);
1833 }
1834
1835 protected String getFieldTypeXPathExpressions() {
1836
1837 String expression = stepsToPath(SCHEMA, FIELD_TYPE.toLowerCase(Locale.ROOT))
1838 + XPATH_OR + stepsToPath(SCHEMA, FIELD_TYPE)
1839 + XPATH_OR + stepsToPath(SCHEMA, TYPES, FIELD_TYPE.toLowerCase(Locale.ROOT))
1840 + XPATH_OR + stepsToPath(SCHEMA, TYPES, FIELD_TYPE);
1841 return expression;
1842 }
1843 }